#include "ap\ap_comm.h"

#include "ds_mt_string.h"

DECLARE_MT_STR_GLOBALS(char, FALSE)
DECLARE_MT_STR_GLOBALS(char, TRUE)
DECLARE_MT_STR_GLOBALS(wchar_t, FALSE)
DECLARE_MT_STR_GLOBALS(wchar_t, TRUE)

// helpers

template<class CH>
static const CH* GetStrWithLength(const CH *str, int &len)
{
   if (len == 0) {
      return NULL;
   }

   if (str == NULL || *str == 0) {
      return NULL;
   }

   if (len < 0) {
      len = 0x7FFFFFFF;
   }

   int i;
   for (i = 0; i < len; i++) {
      if (!str[i]) {
         break;
      }
   }
   len = i;

   return str;
}

// Strings System Functions

void StringsThreadInit(void)
{
   dsMT_STRING<char, FALSE>::ThreadInit();
   dsMT_STRING<char, TRUE>::ThreadInit();
   dsMT_STRING<wchar_t, FALSE>::ThreadInit();
   dsMT_STRING<wchar_t, TRUE>::ThreadInit();

   return;
}

void StringsThreadTerm(void)
{
   dsMT_STRING<char, FALSE>::ThreadTerm();
   dsMT_STRING<char, TRUE>::ThreadTerm();
   dsMT_STRING<wchar_t, FALSE>::ThreadTerm();
   dsMT_STRING<wchar_t, TRUE>::ThreadTerm();

   return;
}

void StringsThreadCheckPoint(void)
{
   dsMT_STRING<char, FALSE>::ThreadCheckPoint();
   dsMT_STRING<char, TRUE>::ThreadCheckPoint();
   dsMT_STRING<wchar_t, FALSE>::ThreadCheckPoint();
   dsMT_STRING<wchar_t, TRUE>::ThreadCheckPoint();

   return;
}

// dsMT_STRING implementation

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT>::dsMT_STRING(void)
{
   InitEmpty();

   return;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT>::dsMT_STRING(int reserve)
{
   InitReserve(reserve);
   
   return;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT>::dsMT_STRING(const CH *str, int strLen, int addStrLen)
{
   Assign(str, strLen, addStrLen);   

   return;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT>::dsMT_STRING(const dsMT_STRING &str)
{
   Assign(str);

   return;
}


template<class CH, BOOL isMT>
MT_STR_OP_TPL
dsMT_STRING<CH, isMT>::dsMT_STRING(const MT_STR_OP &str)
{
   Assign(str);

   return;
}

template<class CH, BOOL isMT>
const CH* dsMT_STRING<CH, isMT>::CStr(int &len) const
{
   const CH     *s;
   const HEADER *hdr;
   strRef.GetData(hdr, s);

   len = hdr->strLen;

   return s;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::InitReserve(int len)
{
   ASSERT(!isMT || len == 0); // reserving space is not correct in multi-threaded mode

   HEADER  *header;
   CH      *s = strRef.BranchLock(len + 1, header);

   s[0] = 0;
   header->strLen = 0;

   strRef.Unlock(s);

   return;
}


template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::InitEmpty(void)
{
   STRONG_ASSERT(emptyStr != NULL);
   strRef.Attach(emptyStr->strRef);

   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::Assign(const dsMT_STRING<CH, isMT> &str)
{
   strRef.Attach(str.strRef);

   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::Assign(const dsMT_STRING<CH, !isMT> &str)
{
   int len = str.Length();
   
   HEADER   *header;
   CH       *s = strRef.BranchLock(len + 1, header, FALSE);

   memcpy(s, str.CStr(), len * sizeof(CH));
   s[len] = 0;

   header->strLen = len;

   strRef.Unlock(s);

   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::Assign(const CH *str, int len, int addLen)
{
   if (str == NULL || str[0] == 0 || len == 0) {
      if (addLen == 0 || isMT) {
         InitEmpty();
      } else {
         InitReserve(addLen);
      }

      return;
   }

   if (len < 0) {
      len = 0x7FFFFFFF;
   }

   // scan str range 0-len for \0 CH
   int i;
   for (i = 0; i < len; i++) {
      if (!str[i]) {
         break;
      }
   }
   len = i;

   HEADER   *header;
   CH       *s = strRef.BranchLock(len + addLen + 1, header, FALSE);

   memcpy(s, str, len * sizeof(CH));
   s[len] = 0;

   header->strLen = len;

   strRef.Unlock(s);

   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::Reserve(int maxLen)
{
   ASSERT(!isMT); // Function cannot be used in multi-threaded mode

   if (isMT) {
      return;
   }

   if (strRef.GetLength() >= maxLen) {
      return;
   }

   int len = strRef.GetRec().strLen;

   HEADER   *header;
   CH       *s = strRef.BranchLock(maxLen, header);
   strRef.Unlock(s);

   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::Compact(bool noDup)
{
   ASSERT(!isMT); // Function cannot be used in multi-threaded mode

   if (isMT) {
      return;
   }

   int len = strRef.GetRec().strLen;

   if (strRef.GetLength() <= len + 1) {
      return;
   }

   strRef.Compact(len + 1, noDup);

   return;
}

template<class CH, BOOL isMT>
CH* dsMT_STRING<CH, isMT>::DiscardLock(int strLenReq)
{
   HEADER   *header;
   CH       *s = strRef.DiscardLock(strLenReq + 1, header, FALSE);

   return s;
}

template<class CH, BOOL isMT>
CH* dsMT_STRING<CH, isMT>::Lock(int strLenReq)
{
   HEADER   *header;
   CH       *s = strRef.BranchLock(strLenReq + 1, header, FALSE);

   return s;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::Unlock(CH* str)
{
   if (str == NULL) {
      STRONG_ASSERT(!isMT);
      str = const_cast<CH*>(strRef.GetData());
   }

   HEADER &header = dsREF_CONTAINER<HEADER, CH, isMT>::RecFromData(str);
   int length = dsREF_CONTAINER<HEADER, CH, isMT>::LengthFromData(str);

   int i;
   for (i = 0; i < length; i++) {

      if (str[i] == 0) {
         header.strLen = i;
         break;
      }
   }

   if (i >= length) {
      header.strLen = length - 1;
      str[header.strLen] = 0;
   }

   strRef.Unlock(str);

   return;
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL
const dsMT_STRING<CH, isMT>& dsMT_STRING<CH, isMT>::operator = (const MT_STR_OP &str)
{
   Assign(str);
   return *this;
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL
BOOL dsMT_STRING<CH, isMT>::operator == (const MT_STR_OP &str) const
{
   int len;
   const CH *s = CStr(len);


   int len2;
   const CH *s2 = str.CStr(len2);

   if (s == s2) {
      return TRUE;
   }

   if (len != len2) {
      return FALSE;
   }

   return memcmp(s, s2, len * sizeof(CH)) == 0;
}

template<class CH, BOOL isMT>
BOOL dsMT_STRING<CH, isMT>::operator == (const CH *str) const
{
   const HEADER *hdr;
   const CH     *s;
   int len = strRef.GetData(hdr, s);

   return strcmp(strRef.GetData(), str) == 0;
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL
BOOL dsMT_STRING<CH, isMT>::operator != (const MT_STR_OP &str) const
{
   return !(*this == str);
}

template<class CH, BOOL isMT>
BOOL dsMT_STRING<CH, isMT>::operator != (const CH *str) const
{
   return !(*this == str);
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL
BOOL dsMT_STRING<CH, isMT>::operator > (const MT_STR_OP &str) const
{
   const CH *s1 = CStr();
   const CH *s2 = str.CStr();

   return strcmp(s1, s2) > 0;
}

template<class CH, BOOL isMT>
BOOL dsMT_STRING<CH, isMT>::operator > (const CH *str) const
{
   return strcmp(CStr(), str) > 0;
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL
BOOL dsMT_STRING<CH, isMT>::operator < (const MT_STR_OP &str) const
{
   const CH *s1 = CStr();
   const CH *s2 = str.CStr();

   return strcmp(s1, s2) < 0;
}

template<class CH, BOOL isMT>
BOOL dsMT_STRING<CH, isMT>::operator < (const CH *str) const        
{
   return strcmp(CStr(), str) < 0;
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL
BOOL dsMT_STRING<CH, isMT>::operator >= (const MT_STR_OP &str) const
{
   const CH *s1 = CStr();
   const CH *s2 = str.CStr();

   return strcmp(s1, s2) >= 0;
}

template<class CH, BOOL isMT>
BOOL dsMT_STRING<CH, isMT>::operator >= (const CH *str) const
{
   return strcmp(CStr(), str) >= 0;
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL
BOOL dsMT_STRING<CH, isMT>::operator <= (const MT_STR_OP &str) const
{
   const CH *s1 = CStr();
   const CH *s2 = str.CStr();

   return strcmp(s1, s2) <= 0;
}

template<class CH, BOOL isMT>
BOOL dsMT_STRING<CH, isMT>::operator <= (const CH *str) const
{
   return strcmp(CStr(), str) <= 0;
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL 
dsMT_STRING<CH, isMT>& dsMT_STRING<CH, isMT>::Insert(int pos, const MT_STR_OP &opStr)
{
   int len;
   const CH *str = opStr.CStr(len);

   Insert(pos, str, len);

   return *this;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT>& dsMT_STRING<CH, isMT>::Insert(int pos, const CH *str, int len)
{
   str = GetStrWithLength(str, len);

   if (str == NULL) {
      return *this;
   }

   ASSERT(pos >=0 && pos <= Length());

   if (isMT) {

      int srcLen;
      const CH *src = CStr(srcLen);

      HEADER *header;
      CH *s = strRef.DiscardLock(srcLen + len + 1, header);

      memcpy(s, src, pos * sizeof(CH));
      memcpy(s + pos, str, len * sizeof(CH));
      memcpy(s + pos + len, src + pos, (srcLen - pos) * sizeof(CH));
      s[srcLen + len] = 0;

      strRef.Unlock(s);

   } else {

      int srcLen = Length();
      HEADER *header;
      CH *s = strRef.BranchLock(srcLen + len + 1, header);

      memmove(s + pos + len, s + pos, (srcLen - pos) * sizeof(CH));
      memcpy(s + pos, str, len * sizeof(CH));
      s[srcLen + len] = 0;

      strRef.Unlock(s);

   }

   return *this;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT>& dsMT_STRING<CH, isMT>::Insert(int pos, CH c, int num)
{
   if (num <= 0 || c == 0) {
      return *this;
   }

   ASSERT(pos >=0 && pos <= Length());

   if (isMT) {

      int srcLen;
      const CH *src = CStr(srcLen);

      HEADER *header;
      CH *s = strRef.DiscardLock(srcLen + num + 1, header);

      memcpy(s, src, pos * sizeof(CH));
      for (int i = 0; i < num; i++) {
         s[pos + i] = c;
      }
      memcpy(s + pos + num, src + pos, (srcLen - pos) * sizeof(CH));
      s[srcLen + num] = 0;

      strRef.Unlock(s);

   } else {

      int srcLen = Length();
      HEADER *header;
      CH *s = strRef.BranchLock(srcLen + num + 1, header);

      memmove(s + pos + num, s + pos, (srcLen - pos) * sizeof(CH));
      for (int i = 0; i < num; i++) {
         s[pos + i] = c;
      }
      s[srcLen + num] = 0;

      strRef.Unlock(s);

   }

   return *this;
}

template<class CH, BOOL isMT>
MT_STR_OP_TPL 
dsMT_STRING<CH, isMT>& dsMT_STRING<CH, isMT>::Append(const MT_STR_OP &opStr)
{
   int len;
   const CH *str = opStr.CStr(len);

   Append(str, len);

   return *this;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT>& dsMT_STRING<CH, isMT>::Append(const CH *str, int len)
{
   str = GetStrWithLength(str, len);

   if (str == NULL) {
      return *this;
   }

   if (isMT) {

      int srcLen;
      const CH *src = CStr(srcLen);

      HEADER *header;
      CH *s = strRef.DiscardLock(srcLen + len + 1, header);

      memcpy(s, src, srcLen * sizeof(CH));
      memcpy(s + len, src, len * sizeof(CH));
      s[srcLen + len] = 0;

      strRef.Unlock(s);

   } else {

      int srcLen = Length();
      HEADER *header;
      CH *s = strRef.BranchLock(srcLen + len + 1, header);

      memcpy(s + srcLen, str, len * sizeof(CH));
      s[srcLen + len] = 0;

      strRef.Unlock(s);

   }

   return *this;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT>& dsMT_STRING<CH, isMT>::Append(CH c, int num)
{
   if (num <= 0 || c == 0) {
      return *this;
   }

   if (isMT) {

      int srcLen;
      const CH *src = CStr(srcLen);

      HEADER *header;
      CH *s = strRef.DiscardLock(srcLen + num + 1, header);

      memcpy(s, src, srcLen * sizeof(CH));

      for (int i = 0; i < num; i++) {
         s[srcLen + i] = c;
      }

      s[srcLen + num] = 0;

      strRef.Unlock(s);

   } else {

      int srcLen = Length();
      HEADER *header;
      CH *s = strRef.BranchLock(srcLen + num + 1, header);

      for (int i = 0; i < num; i++) {
         s[srcLen + i] = c;
      }

      s[srcLen + num] = 0;

      strRef.Unlock(s);

   }

   return *this;
}

template<class CH, BOOL isMT>
CH& dsMT_STRING<CH, isMT>::At(int idx)
{
   STRONG_ASSERT(!isMT); // shouldn't be used in MT mode

   const HEADER *header = &strRef.GetRec();
   HEADER *hdr;
   
   CH *s = strRef.BranchLock(header->strLen + 1, hdr, FALSE);
   strRef.Unlock(s);

   _ASSERT(idx >= 0 && idx <= header->strLen);
   return s[idx];
}

template<class CH, BOOL isMT>
typename dsMT_STRING<CH, isMT>::ST_STRING dsMT_STRING<CH, isMT>::SubStr(int pos, int len) const
{
   const HEADER *header;
   CH *data;

   strRef.GetData(header, data);

   // check pos & len in range
   _ASSERT(pos >=0 && pos <= header->strLen);
   if (len == -1) {
      len = header->strLen - pos;
   }
   if (len == 0) {
      return ST_STRING();
   }
   _ASSERT(pos + num <= pBuffer->strLen);
   // get substring

   return ST_STRING(data + pos, len);
}

template<class CH, BOOL isMT>
typename dsMT_STRING<CH, isMT>::ST_STRING dsMT_STRING<CH, isMT>::Lower(void) const
{
/*   strRef.BranchLock(

   for (int i = 0; i < Length(); i++) {
      pBuffer->str[i] = tolower(pBuffer->str[i]);
   }*/


   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::ToLower(void)
{
   return;
}

template<class CH, BOOL isMT>
typename dsMT_STRING<CH, isMT>::ST_STRING dsMT_STRING<CH, isMT>::Upper(void) const
{
   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::ToUpper(void)
{
   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::Trim(void)
{
   return;
}

template<class CH, BOOL isMT>
typename dsMT_STRING<CH, isMT>::ST_STRING dsMT_STRING<CH, isMT>::EscForm(void)
{
   return;
}

template<class CH, BOOL isMT>
typename dsMT_STRING<CH, isMT>::ST_STRING dsMT_STRING<CH, isMT>::UnEscForm(void)
{
   return;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT> &dsMT_STRING<CH, isMT>::Clear(void)
{
   return;
}

template<class CH, BOOL isMT>
dsMT_STRING<CH, isMT> &dsMT_STRING<CH, isMT>::Erase(int pos, int num)
{
   return;
}


// dsMT_STRING threading initialization functions

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::ThreadInit(void)
{
   dsREF_CONTAINER<HEADER, CH, isMT>::ThreadInit();

   ASSERT(emptyStr == NULL);

   if (isMT) {
      emptyStr = &mtEmptyStr;
   } else {
      emptyStr = apNEW dsMT_STRING<CH, isMT>(0);
   }

   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::ThreadTerm(void)
{
   dsREF_CONTAINER<HEADER, CH, isMT>::ThreadTerm();

   ASSERT(emptyStr != NULL);

   if (emptyStr != NULL) {
      if (isMT) {
         emptyStr = NULL;
      } else {
         delete emptyStr;
         emptyStr = NULL;
      }
   }

   return;
}

template<class CH, BOOL isMT>
void dsMT_STRING<CH, isMT>::ThreadCheckPoint(void)
{
   dsREF_CONTAINER<HEADER, CH, isMT>::ThreadThink();

   return;
}

void test_func(void) {
   StringsThreadInit();

   {
      dsMT_STRING<char, FALSE> a = "hello";
      dsMT_STRING<char, TRUE>  b = a;
      dsMT_STRING<char, TRUE>  c = b;

      char *s = a.Lock(10);
      a.Unlock(s);

      s = b.Lock(10);
      b.Unlock(s);

      a.Compact();
      b.Compact();
      c = a;
      b = c;

      a < a;  a <= a;  a > a;   a >= a;  a == a;   a != a;
      a < b;  a <= b;  a > b;   a >= b;  a == b;   a != b;
      b < b;  b <= b;  b > b;   b >= b;  b == b;   b != b;
      a < c;  a <= c;  a > c;   a >= c;  a == c;   a != c;

      a.Append(b);
      b.Append(a);

      a.Append("hi!");
      b.Append("hi!");

      a.Append('c', 3);
      b.Append('c', 3);

      a.At(1) = 'g';

      a.Insert(5, b);
      b.Insert(5, c);
      c.Insert(5, a);

      a.Insert(3, "Shit");
      b.Insert(3, "Shit");

      a.Insert(7, 'c', 3);
      c.Insert(7, 'c', 3);
   }

   StringsThreadCheckPoint();

   StringsThreadTerm();
};